// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: linchuan

#include "coin2/exchange/gdax/api_order/native_rest_async_asio_subscriber.h"

#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>

#include <nlohmann/json.hpp>

#include "coin2/base/log.h"
#include "coin2/exchange/gdax/api_util/transfers_account_store.h"

namespace coin2::exchange::gdax::api_order {

const std::string& GdaxRestAsyncAsioSubscriber::next_native_symbol() {
  if (native_symbol_list_.empty()) {
    for (const auto& s : native_symbols()) {
      native_symbol_list_.push_back(s);
    }
  }
  CHECK(!native_symbol_list_.empty());
  const int idx = std::rand() % native_symbol_list_.size();
  return native_symbol_list_.at(idx);
}

void GdaxRestAsyncAsioSubscriber::QueryBalance() {
  auto http_context = rest_client()->QueryAccountBalance();
  if (!http_context) return;
  http_context->handler =
      std::bind(&GdaxRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void GdaxRestAsyncAsioSubscriber::QueryOpenOrders(const std::string& native_symbol) {
  RestParams params;
  params.set_product_id(native_symbol);
  auto http_context = rest_client()->QueryOrders(params);
  if (!http_context) return;
  http_context->handler =
      std::bind(&GdaxRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void GdaxRestAsyncAsioSubscriber::QueryAllOrders(const std::string& native_symbol) {
  RestParams params;
  if (!native_symbol.empty()) {
    params.set_product_id(native_symbol);
  }
  params.set_status("all");
  auto http_context = rest_client()->QueryOrders(params);
  if (!http_context) return;
  http_context->handler =
      std::bind(&GdaxRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void GdaxRestAsyncAsioSubscriber::QueryFills() {
  RestParams params;
  params.set_product_id(next_native_symbol());
  auto http_context = rest_client()->QueryTrades(params);
  if (!http_context) return;
  http_context->handler =
      std::bind(&GdaxRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void GdaxRestAsyncAsioSubscriber::QueryTransfer() {
  RestParams params;
  auto http_context = rest_client()->QueryDeposit(params);
  if (!http_context) return;
  http_context->handler =
      std::bind(&GdaxRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);

  http_context = rest_client()->QueryWithdrawal(params);
  if (!http_context) return;
  http_context->handler =
      std::bind(&GdaxRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);

  if (!GdaxTransfersAccountStore::GetInstance().is_initialized()) {
    http_context = rest_client()->TransferInit();
    if (!http_context) return;
    http_context->handler =
        std::bind(&GdaxRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
    http_driver_->PostAsyncContext(http_context);
    return;
  }

  const auto& id_currency_map = GdaxTransfersAccountStore::GetInstance().GetCurrencyIdMap();
  for (const auto& [id, _] : id_currency_map) {
    params.set_currency_id(id);
    http_context = rest_client()->QueryTransfer(params);
    if (!http_context) return;
    http_context->handler =
        std::bind(&GdaxRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
    http_driver_->PostAsyncContext(http_context);
  }
}

void GdaxRestAsyncAsioSubscriber::SubscribeAsync() {
  AddPeriodicTask(
      std::bind(&GdaxRestAsyncAsioSubscriber::QueryBalance, this),
      config_.query_account_balance_period_sec(),
      "query_balance");

  if (is_readonly_order_system_ && symcache_->is_spot()) {
    AddPeriodicTask(
        std::bind(&GdaxRestAsyncAsioSubscriber::QueryTransfer, this),
        config_.query_account_balance_period_sec(),
        "query_transfer");
  }

  AddPeriodicTask(
      std::bind(&GdaxRestAsyncAsioSubscriber::QueryOpenOrders, this, ""),
      config_.query_open_orders_period_sec(),
      "query_open_orders");

  AddPeriodicTask(
      std::bind(&GdaxRestAsyncAsioSubscriber::QueryAllOrders, this, ""),
      config_.query_cancel_orders_period_sec(),
      "query_all_orders");

  AddPeriodicTask(
      std::bind(&GdaxRestAsyncAsioSubscriber::QueryFills, this),
      config_.query_fill_period_sec(),
      "query_fill");
}

}  // namespace coin2::exchange::gdax::api_order
